package com.sweet.imserver.server;

import com.sweet.imserver.MainVerticle;
import com.sweet.imserver.db.DBVerticle;
import com.sweet.imserver.model.RespData;
import io.netty.util.internal.StringUtil;
import io.vertx.core.*;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.file.AsyncFile;
import io.vertx.core.file.FileSystem;
import io.vertx.core.http.HttpServerFileUpload;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.impl.Utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mapdb.DB;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.UUID;

public class HttpIMServer extends AbstractVerticle {
  private final Logger logger = LogManager.getLogger(HttpIMServer.class);


  private static final String ERROR_INFO = "请输入正确参数";

  private static final String contentTypeValue = "application/json;charset=UTF-8";
  private static final String contentTypeName = "Content-Type";
  private static final String downloadFileUrl = "/dFile";
  private static final String uploadFileStore = "fileUpload";

  private static final String pathPrefix = "/v0";


  private EventBus eventBus;

  private FileSystem fileSystem;

  @Override
  public void start(Promise<Void> startPromise) throws Exception {
    eventBus = vertx.eventBus();
    fileSystem = vertx.fileSystem();
    vertx.createHttpServer().requestHandler(req -> {
      final String path = req.path();
      System.out.println(path);


      switch (path) {
        case pathPrefix + "/login":
          req.bodyHandler(buf -> {
            login(buf.toJsonObject(), req.response());
          });
          break;

        case pathPrefix + "/queryOfflineMessage":
          req.bodyHandler(buf -> {
            eventBus.<JsonObject>request(DBVerticle.QUERY_OFFLINE_MESSAGE_EVENT_BUS,
              buf.toJsonObject(), res -> {
                if (res.succeeded()) {
                  JsonObject respBody = res.result().body();
                  req.response().putHeader(contentTypeName, contentTypeValue)
                    .end(RespData.success(respBody).toJson());
                } else {
                  req.response()
                    .putHeader(contentTypeName, contentTypeValue)
                    .end(RespData.fail(ERROR_INFO).toJson());
                }
              });
          });
          break;

        case pathPrefix + "/friend/add":
          req.bodyHandler(buf -> {
            req.response()
              .putHeader(contentTypeName, contentTypeValue)
              .end(addFriend(buf.toJsonObject()));
          });
          break;

        case pathPrefix + "/search":
          req.bodyHandler(buf -> {
            logger.debug("buf: " + buf);
            eventBus.<JsonObject>request(DBVerticle.USER_SEARCH,
              new JsonObject().put("type", DBVerticle.USER_SEARCH)
                .put("phone", buf.toJsonObject().getString("phone")), res -> {
                if (res.succeeded()) {
                  req.response()
                    .putHeader(contentTypeName, contentTypeValue)
                    .end(RespData.success(res.result().body()).toJson());
                } else {
                  req.response()
                    .putHeader(contentTypeName, contentTypeValue)
                    .end(RespData.fail(ERROR_INFO).toJson());
                }
              }
            );
          });
          break;

        case "/fileUpload":
          fileUpload(req);
          break;

        case downloadFileUrl:
          downloadFile(req);
          break;

        default:
          req.response()
            .putHeader(contentTypeName, contentTypeValue)
            .end(RespData.fail(ERROR_INFO).toJson());
      }

      req.exceptionHandler(ex -> {
        logger.error("统一拦截错误，", ex);
        req.response().end(RespData.fail(ERROR_INFO).toJson());
      });

    }).listen(8888, http -> {
      if (http.succeeded()) {
        System.out.println("HTTP server started on port 8888");
        if (fileSystem.existsBlocking(uploadFileStore)) {
          startPromise.complete();
        } else {
          createFileUploadDir(startPromise);
        }
      } else {
        startPromise.fail(http.cause());
      }
    });
  }

  private void createFileUploadDir(Promise<Void> startPromise) {
    fileSystem.mkdir(uploadFileStore, res -> {
      if (res.succeeded()) {
        startPromise.complete();
      } else {
        logger.error("创建文件夹 " + uploadFileStore + "失败，", res.cause());
        startPromise.fail(res.cause());
      }
    });
  }

  private void downloadFile(HttpServerRequest req) {
    String fileId = req.getParam("id");
    if (fileId == null || fileId.length() < 36) {
      req.response().end(RespData.fail("404").toJson());
      return;
    }

    String fileName = uploadFileStore + "/" + fileId;
    fileSystem.exists(fileName, res -> {
      if (res.succeeded() && res.result()) {
        req.response().sendFile(fileName);
      } else {
        req.response().putHeader(contentTypeName, contentTypeValue)
          .end(RespData.fail("404").toJson());
      }
    });
  }

  private void fileUpload(HttpServerRequest req) {
    req.setExpectMultipart(true);
    req.uploadHandler(up -> {
      String fileType = getFileType(up.filename());
      final String filename = UUID.randomUUID().toString() + fileType;
      final String savePath = uploadFileStore + "/" + filename;
      up.streamToFileSystem(savePath)
        .endHandler(end -> {
          req.response()
            .putHeader(contentTypeName, contentTypeValue)
            .end(RespData.success(new JsonObject()
              .put("url", downloadFileUrl + "?id=" + filename))
              .toJson());
        });
    });
  }

  private Future<BufferedImage> getImageAttr(String savePath) {
    Promise<BufferedImage> promise = Promise.promise();

    vertx.executeBlocking(f -> {
      try {
        f.complete(ImageIO.read(new FileInputStream(savePath)));
      } catch (IOException e) {
        f.fail(e);
      }
    }, promise);

    return promise.future();
  }

  private String getFileType(String filename) {
    System.out.println("fileName: " + filename);
    int i = filename.lastIndexOf(".");
    if (i == -1) return "";

    String fileType = filename.substring(i);
    System.out.println(fileType);
    return fileType;
  }

  private String addFriend(JsonObject body) {
    return "friendAdd";
  }

  private void login(JsonObject body, HttpServerResponse response) {
    System.out.println("login : body " + body);
    long time = System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 7);

    eventBus.<JsonObject>request(DBVerticle.LOGIN_EVENT_BUS, body, resp -> {
      if (resp.succeeded()) {
        JsonObject respBody = resp.result().body();
        respBody.put("token", "110").put("expirationTime", time / 1000);
        response
          .putHeader(contentTypeName, contentTypeValue)
          .end(RespData.success(respBody).toJson());
      } else {
        response.putHeader(contentTypeName, contentTypeValue)
          .end(RespData.fail(resp.cause().getMessage()).toJson());
      }
    });
  }
}
